/**********************************************************
 * bike_data.c - contains the code for the header file
 * bike_data.h
 *
 * By: Team 4
 * *******************************************************/


#include "./Includes/bike_data.h"
#include <stdio.h>

/**********************************************************
 * Constants
 * *******************************************************/

const float wheelSize = 2114; //In millimeters
const float mmPerMile = 1.609344e6;
const int secPerMin = 60;
const int secPerHour = 3600;
const float millisecPerMinute = 60.0*1000;
const float millisecPerHour = 60.0*60*1000;

/**********************************************************
 * Initialize - Function to create a BikeData and set all
 * parts to zero and initalize mutex.
 *******************************************************/

void initializeBikeData(BikeDataPtr data){
  data->Speed = 0;  
  data->Cadence = 0;  
  data->HeartRate = 0;  
  data->Calories = 0;

  data->avgSpeed = 0;  
  data->avgCadence = 0;  
  data->avgHeartRate = 0;  

  data->maxSpeed = 0;  
  data->maxCadence = 0;  
  data->maxHeartRate = 0;  

  data->minSpeed = 0;  
  data->minCadence = 0;  
  data->minHeartRate = 0;  

  data->WheelRevolutionCount = 0;

  data->ElapsedTimeHour = 0;
  data->ElapsedTimeMin = 0;
  data->ElapsedTimeMilSec = 0;

  data->Age = 0;
  data->Weight = 0;
  data->Gender = false;
}


/**********************************************************
 * Update Functions - Functions to update the derived parts
 * of the data in the structure from the other parts.
 * Needed to insure synchronization across system.
 * *******************************************************/

//Helper functions for each part updated by the main update
void updateSpeed(BikeDataPtr data, float newSpeed, int elapsedTime, float prevTime){
  // Update the newWheelSPR 
  //float oldSpeed = data->Speed;
  float oldAvgSpeed = data->avgSpeed;

  data->Speed = newSpeed;

  data->avgSpeed = (oldAvgSpeed*prevTime + newSpeed*elapsedTime)/(prevTime+elapsedTime);

  //For both the min in max set the value if it is the
  //first one (prevTime is zero)
  if((newSpeed > data->maxSpeed) || 
    (prevTime == 0)){
    data->maxSpeed = newSpeed;
  }
  if((newSpeed < data->minSpeed) ||
    (prevTime == 0)){
    data->minSpeed = newSpeed;
  }
}


void updateCadence(BikeDataPtr data, float newCadence, int elapsedTime, float prevTime){
  // Update the newPedalSPR 
  //float oldCadence = data->Cadence;
  float oldAvgCadence = data->avgCadence;

  data->Cadence = newCadence;

  data->avgCadence = (oldAvgCadence*prevTime + newCadence*elapsedTime)/(prevTime+elapsedTime);

  //For both the min in max set the value if it is the
  //first one (prevTime is zero)
  if((newCadence > data->maxCadence) ||
    (prevTime == 0)){
    data->maxCadence = newCadence;
  }
  if((newCadence < data->minCadence) ||
    (prevTime == 0)){
    data->minCadence = newCadence;
  }
}


void updateHeartRate(BikeDataPtr data, int newHeartBPM, int elapsedTime, float prevTime){
  // Update the newHeartBPS 
  //float oldHeartBPM = data->HeartRate;
  float oldAvgHeartBPM = data->avgHeartRate;

  data->HeartRate = newHeartBPM;

  data->avgHeartRate = (oldAvgHeartBPM*prevTime + newHeartBPM*elapsedTime)/(prevTime+elapsedTime);

  //For both the min in max set the value if it is the
  //first one (prevTime is zero)
  if((newHeartBPM > data->maxHeartRate) ||
    (prevTime == 0)){
    data->maxHeartRate = newHeartBPM;
  }
  if((newHeartBPM < data->minHeartRate) ||
    (prevTime == 0)){
    data->minHeartRate = newHeartBPM;
  }
}

// Updates the calorie count 

void updateCalories(BikeDataPtr data, int newHeartBPM, int elapsedTime){
  float caloriesPerMinute; 
  float elapsedTimeMin = (float) elapsedTime/millisecPerMinute;
  // change beats per second to beats per minute

  // Calculate the rate of calories being burnt
  if(data->Gender == MALE){
    caloriesPerMinute = (0.6309 * newHeartBPM + 0.09036 * (data->Weight) + 0.2017 * (data->Age) - 55.0969)/4.184;
  }else{
    caloriesPerMinute = (0.4472 * newHeartBPM + 0.05741 * (data->Weight) + 0.074 * (data->Age) -20.4022)/4.184;
  }

  //printf("\ncaloriesPerMinute = %f added = %f ETM = %f",caloriesPerMinute,(float)caloriesPerMinute*elapsedTimeMin,elapsedTimeMin);
  //Increase runing total
  data->Calories += caloriesPerMinute * elapsedTimeMin;
}

//Main update function

void update(BikeDataPtr data, float newSpeed, float newCadence, int newHeartRate,int updateWheelCount, int elapsedTime){
  // Handel time updates before getting the mutex
  float prevTime = getElapsedTime(data);
  //printf("prevTime = %f, elapsedTime = %d\n",prevTime,elapsedTime);

  //Call functions to update each of the parts
  updateSpeed(data,newSpeed,elapsedTime,prevTime);
  updateCadence(data,newCadence,elapsedTime,prevTime);
  updateHeartRate(data,newHeartRate,elapsedTime,prevTime);
  updateCalories(data,newHeartRate,elapsedTime);

  data->WheelRevolutionCount = updateWheelCount;
  incrementElapsedTimeBy(data,elapsedTime);
}

// resets for counter data

void resetCalories(BikeDataPtr data){
  data->Calories = 0;
}

void resetWheelRevolutionCount(BikeDataPtr data){
  data->WheelRevolutionCount = 0;
} 

// increments for counter data

void incrementWheelRevolutionCount(BikeDataPtr data, int update){
  data->WheelRevolutionCount+=update;
} 

//Allows for increaseing the ElapsedTime with a milSec
//increment

void incrementElapsedTimeBy(BikeDataPtr data,int milSec){
  data->ElapsedTimeMilSec+=milSec;

  if(data->ElapsedTimeMilSec > 59999){
    data->ElapsedTimeMin += (long int)data->ElapsedTimeMilSec/60000;
    data->ElapsedTimeMilSec %= 60000;
  }

  if(data->ElapsedTimeMin > 59){
    data->ElapsedTimeHour += (long int)data->ElapsedTimeMin/60;
    data->ElapsedTimeMin %= 60;
  }
}

// resets the elapsed time

void resetElapsedTime(BikeDataPtr data){
  data->ElapsedTimeMilSec = 0;
  data->ElapsedTimeMin = 0;
  data->ElapsedTimeHour = 0;
}

//Allows for setting all the personal data. Called once
//at the start

void setPersonalData(BikeDataPtr data, int age, int weight, bool gender){
  data->Age = age;
  data->Weight = weight;
  data->Gender = gender;
}

/**********************************************************
 * Accessor Functions - Need to access the data structure
 * through accessor functions to ensure synchronization
 * *******************************************************/

//Accessers for speed and inverse speed data

float getSpeed(BikeDataPtr data){
  return data->Speed;
} 

float getCadence(BikeDataPtr data){
  return data->Cadence;
} 

int getHeartRate(BikeDataPtr data){
  return data->HeartRate;
} 

float getCalories(BikeDataPtr data){
  return data->Calories;
}

//Accessers for speed and inverse speed data average

float getAvgSpeed(BikeDataPtr data){
  return data->avgSpeed;
} 

float getAvgCadence(BikeDataPtr data){
  return data->avgCadence;
} 

int getAvgHeartRate(BikeDataPtr data){
  return data->avgHeartRate;
} 

//Accessers for speed and inverse speed data maximum

float getMaxSpeed(BikeDataPtr data){
  return data->maxSpeed;
} 

float getMaxCadence(BikeDataPtr data){
  return data->maxCadence;
} 

int getMaxHeartRate(BikeDataPtr data){
  return data->maxHeartRate;
} 

//Accessers for speed and inverse speed data minimum

float getMinSpeed(BikeDataPtr data){
  return data->minSpeed;
} 

float getMinCadence(BikeDataPtr data){
  return data->minCadence;
} 

int getMinHeartRate(BikeDataPtr data){
  return data->minHeartRate;
} 

// Accessers for counter data

int getWheelRevolutionCount(BikeDataPtr data){
  return data->WheelRevolutionCount;
} 

// getElapsedTime - returns the total time in mili seconds

float getElapsedTime(BikeDataPtr data){
  float results = data->ElapsedTimeMilSec + 
    millisecPerMinute*data->ElapsedTimeMin + 
    millisecPerHour*data->ElapsedTimeHour;
  return results;
}

// getElapsedTimeMilSec - returns the milli-seconds
// part of elapsed time

long int getElapsedTimeMilSec(BikeDataPtr data){
  long int results = data->ElapsedTimeMilSec;
  return results;
} 

// getElapsedTimeMin - returns the minutes
// part of elapsed time

int getElapsedTimeMin(BikeDataPtr data){
  int results = data->ElapsedTimeMin;
  return results;
} 

// getElapsedTimeHour - returns the hour
// part of elapsed time

int getElapsedTimeHour(BikeDataPtr data){
  int results = data->ElapsedTimeHour;
  return results;
} 

// Acessers for personal data

int getAge(BikeDataPtr data){
  int results = data->Age;
  return results;
}

int getWeight(BikeDataPtr data){
  int results = data->Weight;
  return results;
}

bool getGender(BikeDataPtr data){
  int results = data->Gender;
  return results;
}

/**********************************************************
 * Conversion Functions - converts data stored in the 
 * data structure to new forms.
 * *******************************************************/

// HeartRatePercent - converst HeartRateBeatsPerSecond to a zond
// heart rate percent = BPM/max heart rate
// max heart rate ~ 220 - age

float HeartRatePercent(float HeartRateBeatsPerMin, float age){
  return 100*HeartRateBeatsPerMin/(220 - age);
}

// DistanceTraveledMiles - conversts WheelRevolutionCount

float DistanceTraveledMiles(float WheelRevolutionCount){
  return (float) WheelRevolutionCount * wheelSize/mmPerMile;
}

